#include <iostream>
#include <set>
#include <ostream>
#include <math.h>
using namespace std;

class Point{
public:
    Point(int x, int y)
        :_x(x),_y(y)
    {
    }
    // 设置友元输出流函数
    friend ostream & operator<<(ostream & os, const Point & rhs);
    // 设置 < 运算符的重载操作
    friend bool operator<(const Point & ihs,const Point & rhs);

    // 设置成员函数获得点到原点的距离
    double GetSt() const{
        /* sqrt(pow(_ix, 2) + pow(_iy, 2)); */
        // 该函数直接获得
        return hypot(_x, _y);
    }

    int GetX() const{
        return _x;
    }
    int GetY() const{
        return _y;
    }
private:
    int _x;
    int _y;
};
//===========================
// 对输出流进行重载
ostream & operator<<(ostream & os, const Point & rhs){
    cout << "(" 
        << rhs._x 
        << ", " 
        << rhs._y 
        << ")" 
        << endl;

    //=========记得要写返回值，忘记了===================
    return os;
}
//=====================
// 对 < 操作符进行重载
bool operator<(const Point & ihs,const Point & rhs){
    // 设置一个从该点距离远点的距离的成员函数来进行大小的比较
    if(ihs.GetSt() < rhs.GetSt()){// 距离小于直接返回true
        return true; 
    }else if(ihs.GetSt() == rhs.GetSt()){ // 距离相等的情况下
        if(ihs._x < rhs._x){    // ihs._x大返回true
            return true;
        }else if(ihs._x == rhs._x){ // 判断 x 是不是一样
            if(ihs._y < rhs._y){    // 判断 y 
                return true;    
            }else{                  
                return false;
            }
        }else{
            return false;
        }
    }else{
        return false;
    }
}

// 自定义排序模版形式书写
struct Compare{
    // 对()进行重载
    bool operator()(const Point & ihs,  const Point & rhs) const{
        // 设置一个从该点距离远点的距离的成员函数来进行大小的比较
        if(ihs.GetSt() < rhs.GetSt()){// 距离小于直接返回true
            return true;
        }else if(ihs.GetSt() == rhs.GetSt()){ // 距离相等的情况下
            if(ihs.GetX() < rhs.GetX()){    // ihs._x大返回true
                return true;
            }else if(ihs.GetX() == rhs.GetX()){ // 判断 x 是不是一样
                if(ihs.GetY() < rhs.GetY()){    // 判断 y 
                    return true;
                }else{                  
                    return false;
                }
            }else{
                return false;
            }
        }else{
            return false;
        }
    }
};


// 命名空间扩展，进行模版特化
// 命名空间
namespace std{
// 模版特化
template<>
    struct less<Point>{
        bool operator()(const Point & ihs, const Point & rhs) const{
            // 设置一个从该点距离远点的距离的成员函数来进行大小的比较
            if(ihs.GetSt() < rhs.GetSt()){// 距离小于直接返回true
                return true;
            }else if(ihs.GetSt() == rhs.GetSt()){ // 距离相等的情况下
                if(ihs.GetX() < rhs.GetX()){    // ihs._x大返回true
                    return true;
                }else if(ihs.GetX() == rhs.GetX()){ // 判断 x 是不是一样
                    if(ihs.GetY() < rhs.GetY()){    // 判断 y 
                        return true;
                    }else{
                        return false;
                    }
                }else{
                    return false;                     
                }                                                              
            }else{
                return false;
            }

        }

    };

}


// error 错误的形式，set中没有对Point对像的默认排序
void test(){
    // 默认的排序无法对Point进行正确的排序
    // 此时程序编译失败，出现error的错误提示
    // 因为Point在set默认的排序中无法正确的运行

    set<Point> pt = {
        {1,2},
        {1,-2},
        {-1,2},
        {1,2},
        {-1,-2}
    };

    for (auto & ll : pt){
        // 重载 << 输出流运算符 
        cout << ll;
    }
}

void test1(){
    // 默认的排序无法对Point进行正确的排序
    // 因为Point在set默认的排序中无法正确的运行
    // 对set的比较函数中是存在 < 的比较符号的
    // 所以对于operator要进行 < 的重载操作
    // 这个test1对于 < 进行重载操作的实验
    set<Point> pt = {
        {1,2},
        {1,-2},
        {-1,2},
        {1,2},
        {-1,-2}
    };

    for (auto & ll : pt){
        // 重载 << 输出流运算符 
        cout << ll;
    }
}

// 自己写模版进行排序
void test2(){
    // set Compare
    set<Point,Compare> pt = {
        {1,2},
        {1,-2},
        {-1,2},
        {1,2},
        {-1,-2}
    };

    for (auto & ll : pt){
        // 重载 << 输出流运算符 
        cout << ll;
    }
}
// 命名空间扩展，进行模版特化
void test3(){
    set<Point> pt = {
        {1,2},
        {1,-2},
        {-1,2},
        {1,2},
        {-1,-2}
    };

    for (auto & ll : pt){
        // 重载 << 输出流运算符 
        cout << ll;
    }
}
int main(int atgc, char * argv[])
{
    //test();    // 错误的排序未进行排序的手动设定
    //test1();   // < 运算符重载的测试
    //test2();   // 进行自定义模版的排序方式，进行初始化内容
    test3();     // 命名空间扩展，进行模版特化
 
    return 0;
}

